package com.zxq.factory;

import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin;
import com.zxq.factory.annotate.EncoderField;
import com.zxq.factory.annotate.Frame;
import com.zxq.factory.annotate.SerialField;
import com.zxq.factory.encoder.FiledEncoderFactory;
import com.zxq.factory.encoder.IFieldEncoder;
import com.zxq.factory.enums.EField;
import com.zxq.factory.utils.BytesReverse;
import com.zxq.memory.ByteCombination;
import lombok.SneakyThrows;
import sun.net.www.HeaderParser;

import java.lang.reflect.Field;
import java.util.*;

public class EncoderFactory {

    /**
     * 编译成对应的二进制类型
     * @param model 对象数据
     * @return
     */
    @SneakyThrows
    public final static byte[] encoder(Object model) {
        Class cla = model.getClass();
        if(!cla.isAnnotationPresent(Frame.class)){
            System.out.println(cla.getName() +  "没有添加@Frame注解");
            return  new byte[0];
        }
        List<byte[]> bytes = recursionBytes(cla,model);
        byte[] buff = ByteCombination.combinationContent(bytes);
        return buff;
    }

    /**
     * 字段编码，试用的场景，如根据后面计算长度，根据前面计算校验，等等
     * @param value
     * @return
     */
    @SneakyThrows
    public final static byte[] encoderField(Class cla, String fieldName,Object value) {
        byte[] msg = null;
        Field field = cla.getDeclaredField(fieldName);
        EncoderField encoderField =    field.getAnnotation(EncoderField.class);
        if(encoderField.field() != EField.Frame) {
            IFieldEncoder fieldEncoder = FiledEncoderFactory.instance().create(encoderField.field());
            //为空则不编码
            if (value != null) {
                msg = fieldEncoder.encoder(value, encoderField.len());
                SerialField serialField = field.getAnnotation(SerialField.class);
                if(serialField != null) {
                    String serialStr = serialField.serial();
                    if(msg.length != serialStr.length()) {
                        System.out.println("SerialField: check your expression");
                    } else {
                        byte[] tempBuff = new byte[msg.length];
                        int i = 0;
                        do{
                            String strNum = serialStr.substring(i, i+1) + "";
                            Integer index = Integer.parseInt(strNum) - 1;
                            tempBuff[i] = msg[index];
                            i++;
                        }while (i != tempBuff.length);
                        msg = tempBuff;
                        System.out.println("translate:" + HexBin.encode(msg));
                    }
                }

            }
        }
        if(encoderField.field() == EField.Frame) {
            Class frameCla = value.getClass();
            msg = ByteCombination.combinationContent(recursionBytes(frameCla,value));
        }
        return msg;
    }

    /**
     * 递归生成byte[]数组,试用场景在
     * @return
     */
    @SneakyThrows
    private static List<byte[]>  recursionBytes(Class cla, Object model){
        Map<Integer, Field> sequences =  sequenceField(cla);
        List<byte[]> bytes = new ArrayList<>();
        for(Map.Entry<Integer,Field> item: sequences.entrySet()) {
            Field field = item.getValue();
            field.setAccessible(true);
            if(field.isAnnotationPresent(EncoderField.class)) {

                EncoderField encoderField = field.getAnnotation(EncoderField.class);
                // System.out.println("field:" + field.getName());
                if(encoderField.field() != EField.Frame && encoderField.allow()) {
                    IFieldEncoder fieldEncoder = FiledEncoderFactory.instance().create(encoderField.field());
                    Object value = field.get(model);
                    // System.out.println("fieldName:" + field.getName() + ":" + value);
                    //为空则不编码
                    if (value != null) {
                        // System.out.println("value is not null:" + field.getName());
                        byte[] msg = fieldEncoder.encoder(value, encoderField.len());
                        SerialField serialField = field.getAnnotation(SerialField.class);
                        if(serialField != null) {
                            String serialStr = serialField.serial();
                            if(msg.length != serialStr.length()) {
                                System.out.println("SerialField: check your expression");
                            } else {
                                byte[] tempBuff = new byte[msg.length];
                                int i = 0;
                                do{
                                    String strNum = serialStr.substring(i, i + 1);
                                    Integer index = Integer.parseInt(strNum) - 1;
                                    tempBuff[i] = msg[index];
                                    i++;
                                }while (i != tempBuff.length);
                                msg = tempBuff;
                            }
                        }
                        bytes.add(msg);
                    }
                }
                if(encoderField.field() == EField.Frame && encoderField.allow()) {
                    Class frameCla = field.getType();
                    Object value = field.get(model);
                    bytes = recursionBytes(frameCla,value);
                    bytes.addAll(bytes);
                }
            }
        }
        return bytes;
    }



    private static Map<Integer, Field> sequenceField(Class cla) {
        Map<Integer, Field> fieldHash = new TreeMap<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        do{
            Field[] fields = cla.getDeclaredFields();
            for(Field field:fields) {
                if(field.isAnnotationPresent(EncoderField.class)) {
                    EncoderField encoderField = field.getAnnotation(EncoderField.class);
                    int sequence = encoderField.sequence();
                    fieldHash.put(sequence,field);
                }
            }
        } while ((cla = cla.getSuperclass()) != null);
        return fieldHash;
    }



}
